<?php
// +----------------------------------------------------------------------
// | Copyright (c) 2019~2025 https://www.meoyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件，不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 云喵软件开发工作室 【https://www.meoyun.com】
// +----------------------------------------------------------------------

namespace app;

use support\Request;
use Exception;
use InvalidArgumentException;
use RuntimeException;

/**
 * 基础控制器类
 * 
 * 提供通用的CRUD操作、分页查询、搜索条件处理等功能
 * 所有业务控制器应继承此类以获得统一的基础功能
 */
class BaseController
{

    /** 默认分页大小 */
    protected const DEFAULT_PAGE_SIZE = 10;
    
    /** 最大分页大小 */
    protected const MAX_PAGE_SIZE = 1000;
    
    /** 默认排序字段 */
    protected const DEFAULT_ORDER_FIELD = 'created_at';
    
    /** 默认排序方向 */
    protected const DEFAULT_ORDER_DIRECTION = 'desc';
    
    /** 允许的排序方向 */
    protected const ALLOWED_ORDER_DIRECTIONS = ['asc', 'desc'];
    
    /** 字段名正则表达式 */
    protected const FIELD_NAME_PATTERN = '/^[a-zA-Z_][a-zA-Z0-9_]*$/';
    
    // ==================== 属性定义 ====================
    
    /**
     * 数据模型类名
     * 子类必须设置此属性以指定对应的数据模型
     * @var string|null
     */
    protected ?string $dataModel = null;
    
    /**
     * 关联查询配置
     * 定义需要预加载的关联关系
     * @var array
     */
    protected array $with = [];
    
    /**
     * 允许搜索的字段列表
     * 只有在此数组中的字段才会被搜索条件处理
     * @var array
     */
    protected array $allowSearch = [];
    
    /**
     * 搜索字段配置
     * 定义每个搜索字段的搜索类型和处理方式
     * @var array
     */
    protected array $searchConfig = [];

    // ==================== 核心查询方法 ====================

    /**
     * 构建基础查询对象
     * 
     * 创建数据模型的查询构建器实例，并自动添加预设的关联查询
     * 子类可以重写此方法来自定义基础查询逻辑
     * 
     * @return mixed 查询构建器实例
     * @throws RuntimeException 当dataModel未设置时抛出异常
     * @throws InvalidArgumentException 当dataModel不是有效的模型类时抛出异常
     */
    protected function buildBaseQuery(): mixed
    {
        if (!$this->dataModel) {
            throw new RuntimeException('数据模型未设置，请在控制器中设置 $dataModel 属性');
        }
        
        if (!class_exists($this->dataModel)) {
            throw new InvalidArgumentException("数据模型类 {$this->dataModel} 不存在");
        }
        
        try {
            $query = $this->dataModel::query();
            
            // 添加关联查询
            if (!empty($this->with)) {
                $query->with($this->with);
            }
            
            return $query;
        } catch (Exception $e) {
            throw new RuntimeException("创建查询构建器失败: {$e->getMessage()}", 0, $e);
        }
    }

    // ==================== 分页查询方法 ====================

    /**
     * 获取分页数据列表
     * 
     * 处理分页查询请求，支持搜索条件过滤、排序和分页
     * 返回标准化的分页响应格式，包含数据列表和分页信息
     * 
     * @param Request $request HTTP请求对象，包含分页参数、搜索条件等
     * @return mixed 标准化的分页响应数据
     * @throws RuntimeException 当数据模型未设置或查询失败时抛出异常
     * @throws InvalidArgumentException 当请求参数无效时抛出异常
     * 
     * @example
     * // 请求参数示例
     * {
     *     "page": 1,
     *     "pageSize": 20,
     *     "queryForm": {"name": "test", "status": 1},
     *     "orderField": "created_at",
     *     "orderDirection": "desc"
     * }
     */
    public function getListByPage(Request $request): mixed
    {
        try {
            $pageSize = max(1, min(self::MAX_PAGE_SIZE, (int)$request->input('pageSize', self::DEFAULT_PAGE_SIZE)));
            $queryForm = $request->input('queryForm', []);
            $orderField = trim($request->input('orderField', ''));
            $orderDirection = strtolower(trim($request->input('orderDirection', self::DEFAULT_ORDER_DIRECTION)));
            
            // 验证排序方向
            if (!in_array($orderDirection, self::ALLOWED_ORDER_DIRECTIONS)) {
                $orderDirection = self::DEFAULT_ORDER_DIRECTION;
            }
            
            // 验证排序字段
            if (!empty($orderField) && !$this->isValidOrderField($orderField)) {
                $orderField = self::DEFAULT_ORDER_FIELD;
            } elseif (empty($orderField)) {
                $orderField = self::DEFAULT_ORDER_FIELD;
            }
            
            $query = $this->buildBaseQuery();
            $this->handleSearchConditions($query, $queryForm);
            $query->orderBy($orderField, $orderDirection);
            
            $paginate = $query->paginate($pageSize);
            
            return $this->dataByPage($paginate);
        } catch (RuntimeException $e) {
            throw $e;
        } catch (Exception $e) {
            throw new RuntimeException("获取分页数据失败: {$e->getMessage()}", 400, $e);
        }
    }

    // ==================== 分页参数处理 ====================

    /**
     * 验证排序字段是否有效
     * @param string $field
     * @return bool
     */
    protected function isValidOrderField(string $field): bool
    {
        // 基本的字段名验证，防止SQL注入
        return preg_match(self::FIELD_NAME_PATTERN, $field);
    }

    // ==================== 搜索条件处理 ====================

    /**
     * 处理搜索条件
     * @param mixed $query
     * @param array $queryForm
     * @return void
     * @throws InvalidArgumentException
     */
    protected function handleSearchConditions(mixed $query, array $queryForm): void
    {
        $searchWhere = array_intersect(array_keys($queryForm), $this->allowSearch);

        foreach ($searchWhere as $field) {

            $value = $queryForm[$field];

            // 跳过空值
            if ($this->isEmptyValue($value)) {
                continue;
            }

            // 检查字段是否有搜索配置，如果没有则使用默认的精确匹配
            if (!isset($this->searchConfig[$field])) {
                $this->applyExactSearch($query, $field, $value);
                continue;
            }

            // 获取字段的搜索配置
            $config = $this->searchConfig[$field];
            $searchType = $config['type'];

            $this->applySearchCondition($query, $field, $value, $searchType, $config);
        }
    }

    /**
     * 检查值是否为空
     */
    protected function isEmptyValue($value): bool
    {
        return $value === null || $value === '' || (is_array($value) && empty($value));
    }

    /**
     * 应用搜索条件
     * @param mixed $query
     * @param string $field
     * @param mixed $value
     * @param string $searchType
     * @param array $config
     * @return void
     */
    protected function applySearchCondition($query, string $field, $value, string $searchType, array $config): void
    {
        switch ($searchType) {
            case 'like':
                $this->applyLikeSearch($query, $field, $value);
                break;
            case 'start_with':
                $this->applyStartWithSearch($query, $field, $value);
                break;
            case 'end_with':
                $this->applyEndWithSearch($query, $field, $value);
                break;
            case 'gt':
            case 'gte':
            case 'lt':
            case 'lte':
                $this->applyComparisonSearch($query, $field, $value, $searchType);
                break;
            case 'between':
                $this->applyBetweenSearch($query, $field, $value);
                break;
            case 'in':
                $this->applyInSearch($query, $field, $value);
                break;
            case 'not_in':
                $this->applyNotInSearch($query, $field, $value);
                break;
            case 'date_range':
                $this->applyDateRangeSearch($query, $field, $value);
                break;
            default:
                $this->applyExactSearch($query, $field, $value);
                break;
        }
    }

    /**
     * 精确匹配搜索
     */
    protected function applyExactSearch($query, string $field, $value): void
    {
        $query->where($field, '=', $value);
    }

    /**
     * 模糊搜索
     */
    protected function applyLikeSearch($query, string $field, $value): void
    {
        $query->where($field, 'like', '%' . $value . '%');
    }

    /**
     * 开头匹配搜索
     */
    protected function applyStartWithSearch($query, string $field, $value): void
    {
        $query->where($field, 'like', $value . '%');
    }

    /**
     * 结尾匹配搜索
     */
    protected function applyEndWithSearch($query, string $field, $value): void
    {
        $query->where($field, 'like', '%' . $value);
    }

    /**
     * 比较搜索（大于、小于等）
     */
    protected function applyComparisonSearch($query, string $field, $value, string $operator): void
    {
        $operatorMap = ['gt' => '>', 'gte' => '>=', 'lt' => '<', 'lte' => '<='];
        $query->where($field, $operatorMap[$operator], $value);
    }

    /**
     * 范围搜索
     */
    protected function applyBetweenSearch($query, string $field, $value): void
    {
        if (is_array($value) && count($value) === 2) {
            $query->whereBetween($field, [$value[0], $value[1]]);
        }
    }

    /**
     * 处理数组值（用于IN和NOT IN搜索）
     */
    private function processArrayValue($value): array
    {
        return is_array($value) ? $value : array_map('trim', explode(',', $value));
    }

    /**
     * IN搜索
     */
    protected function applyInSearch($query, string $field, $value): void
    {
        $query->whereIn($field, $this->processArrayValue($value));
    }

    /**
     * NOT IN搜索
     */
    protected function applyNotInSearch($query, string $field, $value): void
    {
        $query->whereNotIn($field, $this->processArrayValue($value));
    }

    /**
     * 日期范围搜索
     */
    protected function applyDateRangeSearch($query, string $field, $value): void
    {
        if (!is_array($value) || count($value) !== 2) return;
        
        [$startDate, $endDate] = $value;
        
        if ($startDate && $endDate) {
            $query->whereBetween($field, [$startDate . ' 00:00:00', $endDate . ' 23:59:59']);
        } elseif ($startDate) {
            $query->where($field, '>=', $startDate . ' 00:00:00');
        } elseif ($endDate) {
            $query->where($field, '<=', $endDate . ' 23:59:59');
        }
    }

    // ==================== CRUD操作方法 ====================

    /**
     * 获取数据列表
     * @return mixed
     * @throws RuntimeException
     */
    public function getList(): mixed
    {
        try {
            $query = $this->buildBaseQuery();
            $list = $query->orderBy('id', 'desc')->get();
            return $this->success($list);
        } catch (Exception $e) {
            throw new RuntimeException("获取数据列表失败: {$e->getMessage()}", 0, $e);
        }
    }

    // ==================== 响应处理方法 ====================

    /**
     * 返回成功响应
     */
    public function success($data = null, $message = "SUCCESS", $code = 200): mixed
    {
        $result = ["code" => $code, "message" => $message];
        if ($data !== null) {
            $result["result"] = $data;
        }
        return json($result);
    }

    /**
     * 返回失败响应
     */
    public function fail($message = "FAIL", $code = 400): mixed
    {
        return json(["code" => $code, "message" => $message]);
    }

    // ==================== 分页响应处理 ====================

    /**
     * 返回分页数据响应
     */
    protected function dataByPage($paginate): mixed
    {
        return $this->success([
            'list' => $paginate->items(),
            'total' => $paginate->total(),
            'page' => $paginate->currentPage(),
            'pageSize' => $paginate->perPage(),
            'lastPage' => $paginate->lastPage(),
            'hasMorePages' => $paginate->hasMorePages()
        ]);
    }
}